# This file is part of Gehyra.
#
# Gehyra is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gehyra is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gehyra.  If not, see <http://www.gnu.org/licenses/>.

"""
@package gehyra.common.interfaces
Defines interfaces for components across the codebase.
All interfaces are defined in terms of the zope.interfaces API
$Id: interfaces.py 450 2011-01-20 14:28:47Z andyhhp@gmail.com $
"""

"""
@file gehyra/common/interfaces.py
Defines interfaces for components across the codebase.
"""


from zope.interface import Interface, Attribute

class IManager(Interface):
    """Generic Manager interface"""

    ## Main object
    main = Attribute("""Main""")

    def __init__(main):
        """Constructor.
        Force all objects to take a reference to the main object.
        @param main Main object.  SHould be a GehyraNode or GehyraBridge object
        """

    def initialize():
        """Initalize.
        Used in a similar way to a secondary constructor to control
        the order in which components get initalised.
        """

class IConfigManager(IManager):
    """Configuration Manager Interface.
    Interface definition to provide a uniform way of accessing configuration
    data.  There should be only one configuration manager which anything else in
    the codebase can access.
    """

    ## @brief Dictionary containing the build configuration data
    build = Attribute("""Build configuration data""")
    ## @brief IConfigObject compatable object containing the node data
    node = Attribute("""Node configuration data""")
    ## @brief IConfigObject compatable object containing the network data
    network = Attribute("""Network configuration data""")
    ## @brief IConfigObject compatable object containing the bridge data
    bridge = Attribute("""Bridge configuration data""")


class IConfigObject(Interface):
    """Configuration Object Interface.
    Interface definition for objects providing configuration data to the node.
    Any object bearing this interface should be 'hotplugable' with the
    configuration manager."""

    def load_default(path):
        """Load the default configuration.
        @param path filename to default configuration in gehra.config.defaults.*
        @return boolean indicating whether enough configuration has been loaded 
        to continue.
        """

    def load_real(path):
        """Load the non-default configuration data from its source.
        @param path path to non-default configuration.
        @return boolean indicating whether enough configuration has been loaded
        to continue.
        """

    def save_config():
        """Save configuration data.
        For configuration methods which dont support saving, just return false.
        @return boolean indicating success.
        """

class IBootstrapManager(IManager):
    """IBootstrapManager Interface"""
    

class IBootstrapPuller(Interface):
    """Bootstrap Puller Interface.
    Interface definition for objects which collect bootstrap information for the
    node.
    """

    def __init__(name, **args):
        """Constructor"""

    def initialize():
        """Initialize the object
        @return true if initialized successfully
        @return false if not successfully initialized
        """
    
    def collect():
        """Collect bootstrap configuration information
        Should return a defered object which will eventually return with either
        an error or the bootstrap information
        @return Defered object which will result in the bootstrap information
        """

    def desc():
        """Short description
        @return String giving a consice description of the object for the logs
        """

class IGlobalObserver(Interface):
    """Global Observer Interface.
    Interface for objects which wish to register themselves as an
    observer of global state and events"""
    
    def on_event(event):
        """On Event.
        Called when a global event occurs
        @param event Numeric ID of the current event
        """

    def on_state(state):
        """On State.
        Called when the global state changes
        @param state Numeric ID of the new state
        """
        

class IEntityManager(IManager):
    """Entity Manager Interface"""

class IEntity(Interface):
    """Entity Interface"""

    ## Main object
    main = Attribute("""Main""")
    ## Manager object
    manager = Attribute("""Manager""")
    ## String giving the entity name
    name = Attribute("""Name""")
    ## UUID for referincing the entity
    uuid = Attribute("""UUID""")

    def __init__(main, manager, name, **args):
        """Constructor"""

    def initialize():
        """Initalise.
        Used in a similar way to a secondary constructor to control
        the order in which components get initalised.
        """


def verify_interfaces():
    """Verify all interfaces.
    Because zope lacks the required functionality, this does a depth first
    search over the objectspace, prunes off the irrelevent classes, then
    checks if each interface is implemented by each class, and verifies the
    class if appropriate.  (I know this is a brute force search and if anyone
    can suggest a better way, I am all ears).  All errors are logged as they
    are found.
    @return boolean indicating whether all verification passed."""
    
    import sys
    import zope.interface.interface
    import zope.interface.exceptions
    from zope.interface.verify import verifyClass

    from gehyra.common.logger import LOG

    def generate(obj, _seen = None):
        """Generator to return class types"""
        if _seen is None:
            _seen = set()

        try:
            subs = obj.__subclasses__()
        except TypeError:
            subs = obj.__subclasses__(obj)

        for sub in subs:
            if sub not in _seen:
                _seen.add(sub)
                yield sub
                for sub in generate(sub, _seen):
                    yield sub

    classes = ( x for x in generate(object) if
                str(x).find("gehyra") is not -1 )

    mod = sys.modules[__name__]
    interfaces = [ getattr(mod, x) for x in dir(mod) if
                   isinstance(getattr(mod, x),
                              zope.interface.interface.InterfaceClass) ]
    ret = True

    for c in classes:
        for i in interfaces:
            if i.implementedBy(c):
                try:
                    verifyClass(i, c)
                except (zope.interface.exceptions.BrokenMethodImplementation,
                        zope.interface.exceptions.BrokenImplementation) as e:
                    LOG.error("Object %s - %s" % (c, e))
                    ret = False

    return ret
